#pragma once

#include <windows.h>

// Inflormation about XInput versions
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh405051(v=vs.85).aspx
#ifndef XINPUT_USE_9_1_0
	#pragma message(">>> Using XInput 1.3 (or 1.4)")
#else
	#pragma message(">>> Using XInput 9.1.0")
#endif
#include <XInput.h>
#include <vector>

namespace Xiol
{

/*
	Controller
	This class represents a generic XBox 360 controller. 
	
	Any XBox 360 device, whether it is a gamepad, a wheel, an arcade stick, etc... 
	is represented as an instance of Controller. It's however possible to know
	which particular sub-type the device is.

	The Controller object lets you query the state of the different components present
	on the physical device: the buttons, triggers, thumbsticks and vibration motors.
	It can also be used to set the vibration motors in motion.
	
	Another way to make use of the Controller object is to register a listener to 
	the Controller's events. Whenever a component changes, the Controller fires 
	a notification to the client listener. Note that the listener is not owned by 
	the Controller.
	
	Finally, information is available concerning  the battery level and type if 
	the device is wireless. This information is also provided for an optional headset
	connected to it.
*/
class Controller
{
public:
	enum SubType
	{
		SubType_Gamepad,
		SubType_Wheel,
		SubType_ArcadeStick,
		SubType_FlightStick,
		SubType_DancePad,
		SubType_Guitar,
		SubType_DrumKit,
		SubType_ArcadePad,
		SubType_Count
	};

	enum ComponentTypeID
	{
		ComponentType_Button,
		ComponentType_Trigger,
		ComponentType_Thumbstick,
		ComponentType_VibrationMotor,
		ComponentType_Count
	};

	enum ButtonID
	{
		Button_DPadUp,
		Button_DPadDown,
		Button_DPadLeft,
		Button_DPadRight,
		Button_Start,
		Button_Back,
		Button_LeftThumbstick,
		Button_RightThumbstick,
		Button_LeftShoulder,	
		Button_RightShoulder,
		Button_A,	
		Button_B,
		Button_X,
		Button_Y,
		Button_Count
	};

	enum TriggerID 
	{
		Trigger_Left,
		Trigger_Right,
		Trigger_Count
	};

	enum ThumbstickID
	{
		Thumbstick_Left,
		Thumbstick_Right,
		Thumbstick_Count
	};

	enum VibrationMotorID
	{
		VibrationMotor_Left,
		VibrationMotor_Right,
		VibrationMotor_Count
	};

	enum BatteryType
	{
		BatteryType_Unknown,
		BatteryType_Alkaline,
		BatteryType_NiMH,
		BatteryType_Count
	};

	enum BatteryID
	{
		Battery_Controller,
		Battery_Headset,
		Battery_Count
	};

	DWORD				getControllerIndex() const									{ return mControllerIndex; }
	
	static const char*	getSubTypeName( SubType subType )							{ return mSubTypeName[subType]; }
	const char*			getSubTypeName() const										{ return mSubTypeName[getSubType()]; }
	SubType				getSubType() const											{ return mSubType; }
	
	static const char*	getComponentTypeName( ComponentTypeID componentTypeID )		{ return mComponentTypeName[componentTypeID]; }
	
	static const char*	getButtonName( ButtonID buttonID )							{ return mButtonName[buttonID]; }
	bool				hasButton( ButtonID buttonID ) const						{ return mHasButton[buttonID]; }
	bool				isButtonPressed( ButtonID buttonID ) const					{ return mIsButtonPressed[buttonID]; }
	
	static const char*	getTriggerName( TriggerID triggerID )						{ return mTriggerName[triggerID]; }
	bool				hasTrigger( TriggerID triggerID ) const						{ return mHasTrigger[triggerID]; }
	BYTE				getTriggerPosition( TriggerID triggerID ) const				{ return mTriggerPosition[triggerID]; }

	static const char*	getThumbstickName( ThumbstickID thumbstickID )				{ return mThumbstickName[thumbstickID]; }
	bool				hasThumbstick( ThumbstickID thumbstickID ) const			{ return mHasThumbstick[thumbstickID]; }
	void				getThumbstickPosition( ThumbstickID thumbstickID, SHORT& positionX, SHORT& positionY ) const { positionX = mThumbstickXPosition[thumbstickID];	positionY = mThumbstickYPosition[thumbstickID]; }
	
	static const char*	getVibrationMotorName( VibrationMotorID motorID )			{ return mVibrationMotorName[motorID]; }
	bool				hasVibrationMotor( VibrationMotorID motorID )				{ return mHasVibrationMotor[motorID]; }
	WORD				getVibrationMotorSpeed( VibrationMotorID motorID ) const	{ return mVibrationMotorSpeed[motorID]; }
	void				setVibrationMotorSpeed( VibrationMotorID motorID, WORD speed );

	static const char*	getBatteryTypeName( BatteryType batteryType )				{ return mBatteryTypeName[batteryType]; }
	static const char*	getBatteryName( BatteryID batteryID )						{ return mBatteryName[batteryID]; }
	bool				hasBattery( BatteryID batteryID ) const						{ return mHasBattery[batteryID]; }
	BYTE				getBatteryType( BatteryID batteryID  ) const				{ return mBatteryType[batteryID]; }
	static BYTE			getBatteryLevelMax();
	// Returns a value between 0 and getBatteryLevelMax()
	BYTE				getBatteryLevel( BatteryID batteryID  ) const				{ return mBatteryLevel[batteryID]; }

	class Listener
	{
	public:
		virtual void onComponentChanged( Controller* controller, ComponentTypeID componentTypeID, int componentID ) {}
	};

	typedef				std::vector<Listener*> Listeners; 
	void				addListener( Listener* listener );
	bool				removeListener( Listener* listener );
	Listeners			getListeners() const { return mListeners; }

protected:
	friend class ControllerManager;
	Controller( DWORD controllerIndex, const XINPUT_CAPABILITIES& capabilities );
	virtual ~Controller();

	void				setButtonPressed( ButtonID button, bool pressed );
	void				setTriggerPosition( TriggerID trigger, BYTE position );
	void				setThumbstickPosition( ThumbstickID thumbstick, SHORT positionX, SHORT positionY );
	
private:
	static SubType		xinputSubTypeToSubType( int xinputSubType );
	void				update( const XINPUT_STATE& state );
	void				updateBatteryInformation();
	void				setBatteryInformation( BatteryID batteryID, BatteryType batteryType, BYTE batteryLevel );
	void				clearBatteryInformation( BatteryID batteryID );
	static bool			xinputBatteryTypeToBatteryType( BYTE xinputBatteryType, Controller::BatteryType& batteryType );

	static const char*	mSubTypeName[SubType_Count];
	static const char*	mComponentTypeName[ComponentType_Count];
	static const char*	mButtonName[Button_Count];
	static const WORD	mButtonXInputID[Button_Count];
	static const char*	mTriggerName[Trigger_Count];
	static const char*	mThumbstickName[Thumbstick_Count];
	static const char*	mVibrationMotorName[VibrationMotor_Count];
	static const char*	mBatteryTypeName[BatteryType_Count];
	static const char*	mBatteryName[Battery_Count];
	
	// Controller information
	DWORD				mControllerIndex;
	SubType				mSubType;
	
	// Capabilities
	bool				mHasVoiceSupport;
	bool				mHasButton[Button_Count];		
	bool				mHasTrigger[Trigger_Count];
	bool				mHasThumbstick[Thumbstick_Count];
	bool				mHasVibrationMotor[VibrationMotor_Count];
	bool				mHasBattery[Battery_Count];
	
	// State
	static const unsigned int mBatteryUpdateIntervalInMs = 10000;	
	unsigned int		mNextBatteryUpdateTime;
	DWORD				mLastPacketNumber;	
	bool				mIsButtonPressed[Button_Count];		
	BYTE				mTriggerPosition[Trigger_Count];
	SHORT				mThumbstickXPosition[Thumbstick_Count];
	SHORT				mThumbstickYPosition[Thumbstick_Count];
	WORD				mVibrationMotorSpeed[VibrationMotor_Count];
	BatteryType			mBatteryType[Battery_Count];
	BYTE				mBatteryLevel[Battery_Count];
	
	// Listeners
	Listeners			mListeners;
};

}
